package com.hj.calendar.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hj.calendar.client.CaldavDdClient;
import com.hj.calendar.client.CaldavFsClient;
import com.hj.calendar.dao.CaldavConfigDao;
import com.hj.calendar.domain.constant.CalendarRedisConstant;
import com.hj.calendar.domain.dto.*;
import com.hj.calendar.domain.entity.CaldavConfig;
import com.hj.calendar.domain.enums.CaldavOriginFromEnum;
import com.hj.calendar.domain.enums.DeletedStatEnum;
import com.hj.calendar.domain.model.CaldavModel;
import com.hj.calendar.domain.vo.CaldavAccountVO;
import com.hj.calendar.domain.vo.FsDdCalendarVO;
import com.hj.calendar.service.CaldavService;
import com.hj.mybatis.service.impl.BaseServiceImpl;
import com.hj.redis.RedisUtil;
import com.hj.response.resp.RestfulResult;
import com.hj.security.cache.TokenStoreCache;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author hj
 * @Date 2023/5/22
 * @Description:
 */
@Slf4j
@Service
public class CaldavServiceImpl extends BaseServiceImpl<CaldavConfigDao, CaldavConfig> implements CaldavService {


    @Resource
    private CaldavFsClient fsClient;
    @Resource
    private CaldavDdClient ddClient;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private TokenStoreCache tokenStoreCache;


    @Override
    public RestfulResult<List<CaldavAccountVO>> listAccount() {
        Long userId = tokenStoreCache.getUserId();
        if (null == userId) {
            return RestfulResult.fail("caldav账号(飞书,钉钉)获取失败,请重新登录");
        }
        List<CaldavAccountVO> voList = new ArrayList<>();
        List<CaldavConfig> list = this.list(Wrappers.<CaldavConfig>lambdaQuery()
                .eq(CaldavConfig::getUserId, userId)
        );
        if (ObjectUtils.isNotEmpty(list)) {
            list.forEach(c -> {
                CaldavAccountVO vo = new CaldavAccountVO();
                BeanUtils.copyProperties(c, vo);
                voList.add(vo);
            });
        }
        return RestfulResult.ok(voList);
    }

    @Override
    public RestfulResult<CaldavAccountVO> accountDetail(DdFsDTO dto) {
        CaldavConfig config = this.getById(dto.getId());
        CaldavAccountVO vo = new CaldavAccountVO();
        BeanUtils.copyProperties(config, vo);
        return RestfulResult.ok(vo);
    }

    @Override
    public RestfulResult<Boolean> accountState(DdFsStateDTO dto) {
        if (null == dto.getId() || null == dto.getState()) {
            return RestfulResult.fail("参数错误");
        }
        CaldavConfig config = this.getById(dto.getId());
        config.setState(dto.getState());
        config.setUpdateTime(Instant.now());
        this.updateById(config);
        return RestfulResult.ok(true);
    }

    @Override
    public RestfulResult<Boolean> accountDelete(DdFsDeletedDTO dto) {
        if (null == dto.getId() || null == dto.getDeleted()) {
            return RestfulResult.fail("参数错误");
        }
        CaldavConfig config = this.getById(dto.getId());
        // 暂时删除不可恢复
        if (null != dto.getDeleted() && DeletedStatEnum.ONE.getState() == dto.getDeleted()) {
            this.removeById(config.getId());
        }
        return RestfulResult.ok(true);
    }

    @Override
    public RestfulResult<CaldavAccountVO> setupAccount(DdFsSetupDTO dto) {
        Long userId = tokenStoreCache.getUserId();
        if (null == userId) {
            return RestfulResult.fail("caldav账号(飞书,钉钉)获取失败,请重新登录");
        }
        Integer originFrom = dto.getOriginFrom();
        String username = dto.getUsername();
        String password = dto.getPassword();
        if (null == originFrom || StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            return RestfulResult.fail("caldav账号操作失败,参数错误,需要账号来源,账号,密码");
        }
        CaldavConfig config;
        Long id = dto.getId();
        if (null != id) {
            config = this.getById(id);
        } else {
            long count = this.count(Wrappers.<CaldavConfig>lambdaQuery()
                    .eq(CaldavConfig::getUserId, userId)
                    .eq(CaldavConfig::getOriginFrom, originFrom)
            );
            if (count > 0) {
                return RestfulResult.fail("caldav此来源账号已经存在,请不要重复添加");
            }
            config = new CaldavConfig();
            config.setUserId(userId);
        }
        CaldavDdFsDTO fsDdDTO = new CaldavDdFsDTO();
        // 飞书钉钉校验账号封装
        fsDdDTO.setUsername(username);
        fsDdDTO.setPassword(password);
        boolean flag = false;
        switch (originFrom) {
            case 1:
                // 自己系统 预约助手
                log.info("来源错误,现在只处理飞书,钉钉");
                config.setOriginFrom(CaldavOriginFromEnum.ONE.getOriginFrom());
                config.setOriginName(CaldavOriginFromEnum.ONE.getOriginName());
                // 处理账号密码
                break;
            case 2:
                log.info("~~~来源飞书:{}", userId);
                config.setOriginFrom(CaldavOriginFromEnum.TWO.getOriginFrom());
                config.setOriginName(CaldavOriginFromEnum.TWO.getOriginName());
                // 校验 用户名 密码
                String fsCalendarUrl = fsClient.getFsCalendarUrl(fsDdDTO);
                if (StringUtils.isNotBlank(fsCalendarUrl)) {
                    config.setPathUuid(fsCalendarUrl);
                    // 获取飞书displayName
                    fsDdDTO.setPathUuid(fsCalendarUrl);
                    String fsDisplayName = fsClient.getFsDisplayName(fsDdDTO);
                    if (StringUtils.isNotBlank(fsDisplayName)) {
                        config.setCalendarName(fsDisplayName);
                        flag = true;
                    }
                    flag = true;
                }
                break;
            case 3:
                log.info("~~~来源钉钉:{}", userId);
                config.setOriginFrom(CaldavOriginFromEnum.THREE.getOriginFrom());
                config.setOriginName(CaldavOriginFromEnum.THREE.getOriginName());
                // 校验 用户名 密码
                String displayName = ddClient.getDdDisplayName(fsDdDTO);
                if (StringUtils.isNotBlank(displayName)) {
                    config.setCalendarName(displayName);
                    flag = true;
                }
                break;
            default:
                log.warn("暂不对接其他日历");
                break;
        }
        if (!flag) {
            //校验不通过,账号密码错误
            return RestfulResult.fail("用户名或者密码错误");
        }
        config.setUsername(username);
        config.setPassword(password);
        if (null != id) {
            // 修改
            this.updateById(config);
        } else {
            this.save(config);
        }
        CaldavAccountVO vo = new CaldavAccountVO();
        BeanUtils.copyProperties(config, vo);
        return RestfulResult.ok(vo);
    }

    @Override
    public RestfulResult<List<FsDdCalendarVO>> fsCalendar(Date selectDay) {
        Long userId = tokenStoreCache.getUserId();
        if (null == userId) {
            return RestfulResult.fail("caldav账号(飞书,钉钉)获取失败,请重新登录");
        }
        String fsCalendarKye = String.format(CalendarRedisConstant.FS_CALENDAR_KYE, userId,
                DateUtil.format(selectDay, DateTimeFormatter.ISO_LOCAL_DATE));
        boolean existKey = redisUtil.exists(fsCalendarKye);
        List<FsDdCalendarVO> voList;
        if (existKey) {
            // 有缓存数据
            String json = redisUtil.get(fsCalendarKye, String.class);
            voList = JSON.parseArray(json, FsDdCalendarVO.class);
        } else {
            CaldavConfig one = this.getOne(Wrappers.<CaldavConfig>lambdaQuery()
                    .eq(CaldavConfig::getUserId, userId)
                    .eq(CaldavConfig::getOriginFrom, CaldavOriginFromEnum.TWO.getOriginFrom())
            );
            if (null == one) {
                return RestfulResult.fail("caldav获取飞书日历失败,请先创建账号信息");
            }
            CaldavDdFsDTO fsDTO = new CaldavDdFsDTO();
            fsDTO.setUsername(one.getUsername());
            fsDTO.setPassword(one.getPassword());
            String fsCalendarUrl = one.getPathUuid();
            if (StringUtils.isBlank(fsCalendarUrl)) {
                fsCalendarUrl = fsClient.getFsCalendarUrl(fsDTO);
                one.setPathUuid(fsCalendarUrl);
                one.setUpdateTime(Instant.now());
                this.updateById(one);
            }
            fsDTO.setPathUuid(fsCalendarUrl);
            // 查询某天
            fsDTO.setStartTime(DateUtil.beginOfDay(selectDay));
            fsDTO.setEndTime(DateUtil.endOfDay(selectDay));
            voList = this.listFsCalendar(fsDTO, selectDay);
            redisUtil.set(fsCalendarKye, JSON.toJSONString(voList), CalendarRedisConstant.CACHE_DURATION, TimeUnit.MINUTES);
        }
        return RestfulResult.ok(voList);
    }

    @Override
    public List<FsDdCalendarVO> listFsCalendar(CaldavDdFsDTO dto, Date selectDay) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future<List<CaldavModel>> future = executorService.submit(() -> fsClient.getCalendar(dto));
        List<CaldavModel> list;
        List<FsDdCalendarVO> voList = new ArrayList<>();
        try {
            list = future.get(3, TimeUnit.SECONDS);
            if (ObjectUtils.isNotEmpty(list)) {
                this.handleFsDdModelToVo(list, voList, CaldavOriginFromEnum.TWO.getOriginFrom(), selectDay);
            }
            return voList;
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            log.warn("获取飞书日历超时:{}", e.getMessage());
            list = fsClient.getCalendar(dto);
            if (ObjectUtils.isNotEmpty(list)) {
                this.handleFsDdModelToVo(list, voList, CaldavOriginFromEnum.TWO.getOriginFrom(), selectDay);
            }
            return voList;
        }
    }

    private void handleFsDdModelToVo(List<CaldavModel> list, List<FsDdCalendarVO> voList, Integer originFrom, Date selectDay) {
        list.forEach(m -> {
            if (null != selectDay) {
                // 飞书日历 要去掉当天往后的日历
                DateTime sd = DateUtil.beginOfDay(selectDay);
                DateTime fd = DateUtil.beginOfDay(m.getStartTime());
                // 不是同一天排除
                if (!sd.equals(fd)) {
                    return;
                }
            }
            FsDdCalendarVO vo = new FsDdCalendarVO();
            vo.setScheduleName(m.getSummary());
            vo.setMeetingStartTime(m.getStartTime());
            vo.setMeetingEndTime(m.getEndTime());
            vo.setMeetingContext(m.getDescription());
            vo.setInitiatorSite(m.getLocation());
            vo.setOriginFrom(originFrom);
            voList.add(vo);
        });
    }

    @Override
    public RestfulResult<List<FsDdCalendarVO>> ddCalendar(Date selectDay) {
        Long userId = tokenStoreCache.getUserId();
        if (null == userId) {
            return RestfulResult.fail("caldav账号(飞书,钉钉)获取失败,请重新登录");
        }
        String ddCalendarKye = String.format(CalendarRedisConstant.DD_CALENDAR_KYE, userId,
                DateUtil.format(selectDay, DateTimeFormatter.ISO_LOCAL_DATE));
        boolean existKey = redisUtil.exists(ddCalendarKye);
        List<FsDdCalendarVO> voList;
        if (existKey) {
            // 有缓存数据
            String json = redisUtil.get(ddCalendarKye, String.class);
            voList = JSON.parseArray(json, FsDdCalendarVO.class);
        } else {
            CaldavConfig one = this.getOne(Wrappers.<CaldavConfig>lambdaQuery()
                    .eq(CaldavConfig::getUserId, userId)
                    .eq(CaldavConfig::getOriginFrom, CaldavOriginFromEnum.THREE.getOriginFrom())
            );
            if (null == one) {
                return RestfulResult.fail("caldav获取钉钉日历失败,请先创建账号信息");
            }
            CaldavDdFsDTO ddDTO = new CaldavDdFsDTO();
            ddDTO.setUsername(one.getUsername());
            ddDTO.setPassword(one.getPassword());
            ddDTO.setStartTime(DateUtil.beginOfDay(selectDay));
            ddDTO.setEndTime(DateUtil.endOfDay(selectDay));
            voList = this.listDdCalendar(ddDTO);
            redisUtil.set(ddCalendarKye, JSON.toJSONString(voList), CalendarRedisConstant.CACHE_DURATION, TimeUnit.MINUTES);
        }
        return RestfulResult.ok(voList);
    }

    @Override
    public List<FsDdCalendarVO> listDdCalendar(CaldavDdFsDTO dto) {
        List<CaldavModel> list = ddClient.getCalendar(dto);
        log.info("钉钉获取日历返回的条数:{}", list.size());
        List<FsDdCalendarVO> voList = new ArrayList<>();
        if (ObjectUtils.isNotEmpty(list)) {
            this.handleFsDdModelToVo(list, voList, CaldavOriginFromEnum.THREE.getOriginFrom(), null);
        }
        return voList;
    }
}
